#set( $symbol_pound = '#' )
#set( $symbol_dollar = '$' )
#set( $symbol_escape = '\' )
package ${package}.basic.websocket;

import ${package}.basic.jmx.JmxTool;
import ${package}.common.bean.vo.JsonResult;
import ${package}.common.emum.ReturnCodeEnum;
import ${package}.common.utils.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.sun.management.OperatingSystemMXBean;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import javax.management.MBeanServerConnection;
import javax.management.remote.JMXConnector;
import java.io.IOException;
import java.lang.management.ClassLoadingMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.lang.management.RuntimeMXBean;
import java.util.Date;
import java.util.Hashtable;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * Description: 无法补充
 *
 * @author wupanhua
 * @date 2019/8/6 15:28
 *
 * <pre>
 *              ${copyright}
 *      Copyright (c) 2019. All Rights Reserved.
 * </pre>
 */
@Slf4j
public class WebSocketServer extends TextWebSocketHandler {

    // IP正则表达式
    static final String ADDRESS_REGEX= "^((25[0-5]|2[0-4]${symbol_escape}${symbol_escape}d|((1${symbol_escape}${symbol_escape}d{2})|([1-9]?${symbol_escape}${symbol_escape}d)))${symbol_escape}${symbol_escape}.){3}(25[0-5]|2[0-4]${symbol_escape}${symbol_escape}d|((1${symbol_escape}${symbol_escape}d{2})|([1-9]?${symbol_escape}${symbol_escape}d)))${symbol_dollar}";
    // 端口号正则表达式
    static final String PORT_REGEX = "^${symbol_escape}${symbol_escape}d{1,5}";

    /**
     * 存储进程信息
     */
    private Hashtable<String, DealWithJvmInfo> tables = new Hashtable<>();

    /**
     * Description:
     * <接受到信息>
     * @author wupanhua
     * @date 14:03 2019/8/7
     * @param session 1
     * @param message 2
     **/
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws IOException {

        // 信息无效
        String messagePayload = message.getPayload();
        if (StringUtils.isEmpty(messagePayload) || !(isIllegalConnectInfo(messagePayload))) {
            session.sendMessage(WebSocketServer.createTextMessage(JSON.toJSONString(JsonResult.getInstant(ReturnCodeEnum.PARAMETER_NOT_MATCH.getCode(), ReturnCodeEnum.PARAMETER_NOT_MATCH.getMsg()))));
        }

        // @TODO 传入的参数分两种,一种新建连接,一种关闭连接

        // 开始处理数据
        JSONObject jsonObject = JSONObject.parseObject(messagePayload);
        JMXConnector jmxConnector = JmxTool.connectRemoteJvm(jsonObject.getString("ip"),jsonObject.getString("port"), jsonObject.getString("user"), jsonObject.getString("password"));

        // 参数1: jmx连接; 参数2: websocket会话; 参数3: 是否开始监听数据(默认true);
        DealWithJvmInfo dw = new DealWithJvmInfo(jmxConnector, session, true, jsonObject.getString("ip"));
        tables.put(jsonObject.getString("ip"), dw);
        dw.run();

    }

    /**
     * Description:
     * <连接建立>
     * @author wupanhua
     * @date 14:03 2019/8/7
     * @param session 1
     **/
    @Override
    public void afterConnectionEstablished(WebSocketSession session){
        log.info("{} 接入webSocket服务器", Objects.requireNonNull(session.getLocalAddress()).getAddress().getHostAddress());
    }

    /**
     * Description:
     * <连接被断开>
     * @author wupanhua
     * @date 14:04 2019/8/7
     * @param session 1
     * @param status 2
     **/
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status){
        Set<Map.Entry<String, DealWithJvmInfo>> s = tables.entrySet();
        for (Map.Entry<String, DealWithJvmInfo>  entry : s) {
            entry.getValue().stop();
        }
    }

    /**
     * Description:
     * <创建一个TextMessage>
     * @author wupanhua
     * @date 14:04 2019/8/7
     * @param payload 1
     * @return org.springframework.web.socket.TextMessage
     **/
    private static TextMessage createTextMessage(String payload) {
        return new TextMessage(payload);
    }

    /**
     * Description:
     * <判断传入的json中包含的连接是否合法>
     * @author wupanhua
     * @date 14:05 2019/8/7
     * @param jsonString 1
     * @return boolean
     **/
    private boolean isIllegalConnectInfo(String jsonString) {

        JSONObject jsonObject = JSONObject.parseObject(jsonString);
        // IP地址
        String ip = jsonObject.getString("ip");
        // 端口号
        String port = jsonObject.getString("port");

        // 判断条件
        if (ip.matches(ADDRESS_REGEX) && port.matches(PORT_REGEX)) {
            return Boolean.TRUE;
        }

        return Boolean.FALSE;
    }

    class DealWithJvmInfo implements Runnable {

        private final JMXConnector jmxConnector;
        private WebSocketSession session;
        private boolean isRunning;
        private String ip;

        DealWithJvmInfo(JMXConnector jmxConnector, WebSocketSession session, Boolean isRunning, String ip) {
            this.jmxConnector = jmxConnector;
            this.session = session;
            this.isRunning = isRunning;
            this.ip = ip;
        }

        @Override
        public void run() {

            while (isRunning) {

                synchronized (this.jmxConnector) {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        log.error("error info ",e);
                    }

                    try {

                         // 堆内存监控数据格式
                         // {'ip':'192.168.0.12', time:'10:03', 'max':1000, 'used': 1000, 'cpu':0.1}
                        MBeanServerConnection mbc = jmxConnector.getMBeanServerConnection();
                        Map<String, Object> res = Maps.newHashMap();

                        // 堆内存使用情况
                        MemoryMXBean mm = ManagementFactory.getPlatformMXBean(mbc, MemoryMXBean.class);
                        MemoryUsage mu = mm.getHeapMemoryUsage();
                        res.put("ip", this.ip);
                        res.put("time", DateUtil.format(new Date(), DateUtil.DateStyle.HH_DOT_MM_DOT_SS));
                        res.put("max", mu.getMax() / 1024 / 1024);
                        res.put("used", mu.getUsed() / 1024 / 1024);
                        res.put("committed", mu.getCommitted() / 1024 / 1024);
                        MemoryUsage nonHeapMemoryUsage = mm.getNonHeapMemoryUsage();
                        res.put("nonCommitted", nonHeapMemoryUsage.getCommitted() / 1024 / 1024);
                        res.put("nonHeapUsed", nonHeapMemoryUsage.getUsed() / 1024 / 1024);

                        // jvm使用cpu使用率
                        OperatingSystemMXBean operatingSystemMxBean = ManagementFactory.getPlatformMXBean(mbc, OperatingSystemMXBean.class);
                        res.put("cpu", String.valueOf(operatingSystemMxBean.getProcessCpuLoad()));
                        // 获取可使用的处理器数量
                        res.put("processors", operatingSystemMxBean.getAvailableProcessors());
                        // 获得系统负载平均值
                        res.put("sla", operatingSystemMxBean.getSystemLoadAverage());

                        // 类的使用情况
                        ClassLoadingMXBean classLoadingMxBean = ManagementFactory.getPlatformMXBean(mbc, ClassLoadingMXBean.class);
                        res.put("loadedClass", classLoadingMxBean.getLoadedClassCount());
                        res.put("unloadedClass", classLoadingMxBean.getUnloadedClassCount());
                        res.put("totalLoadedClass", classLoadingMxBean.getTotalLoadedClassCount());

                        RuntimeMXBean runtimeMxBean = ManagementFactory.getPlatformMXBean(mbc, RuntimeMXBean.class);
                        // PID
                        res.put("pid", runtimeMxBean.getName());
                        // 启动目录
//                        res.put("bootPath", runtimeMXBean.getBootClassPath());
                        // 环境变量
                        res.put("classPath", runtimeMxBean.getClassPath());
                        // 输入参数
                        res.put("input", runtimeMxBean.getInputArguments());
                        // 管理接口规范的版本
                        res.put("specV", runtimeMxBean.getManagementSpecVersion());
                        // Java虚拟机的启动时间
                        res.put("startTime", DateUtil.format(new Date(runtimeMxBean.getStartTime()), DateUtil.DateStyle.ALL));
                        // 系统属性的名称和值的映射
                        res.put("properties", runtimeMxBean.getSystemProperties());

                        if (session.isOpen()) {
                            session.sendMessage(WebSocketServer.createTextMessage(JSON.toJSONString(res)));
                        }
                    } catch (IOException e) {
                        log.error("error info ",e);
                    }
                }
            }

        }

        /**
         * Description:
         * <使用停止循环加抛出异常的方式停止请求数据线程>
         * @author wupanhua
         * @date 14:05 2019/8/7
         **/
        void stop() {
            JmxTool.destoryRemoteConnect(jmxConnector);
            this.isRunning = false;
            log.info("stop websocket....");
        }
    }
}
